linux 启动过程
说起 linux 的启动,用“牵一发动全身”来形容在形象不过了。虽然按下开关后,屏幕仍然是黑黑的,但是实际上程序已经在策马奔腾。
基础部分
- 段说明
- linux 中的各种地址
Linux 操作系统启动流程
在 x86 下,其启动流程如下,arm 平台与 x86 启动不一致。
第一阶段 硬件引导启动
第一阶段主要是硬件引导启动,
x86 平台
CPU 运行在实模式中,其物理地址:
当你按下 power 键的那一刻,CS 寄存器被复位为 0xFFFF,EIP 寄存器被复位为 0,CPU 访问地址为:0xFFFF0,在实模式下,此时还没有突破 A20 线,其可访问的最大地址也就是 0xFFFFF(1Mb),此处只有 16 字节,太小以至于无法存储 bios 代码,所以此处一般为一个跳转指令,跳转到真正的 bios 处,然后在执行 bios 的代码。
在 bios 中,首先会 POST:Power On Self Test, 上电自检,之后对计算机各部件进行初始化,如有错误则报警提示。
下一步在外部存储器中寻找操作系统,找到第一个可启动设备。
在 存储器中,第一个扇区(512 字节)的最后两字节为 0x55, 0xaa,则称该存储器为可启动设备
接着将第一个可启动设备的第一个扇区拷贝到 0x7c00 处,跳到 0x7c00 处执行。
IBM 钦定,1981 年发布的 IBM PC 5150,其内存大小是 32KB,
0x7c00 是 32KB 内存的倒数 1024 字节,从 0x7c00 的前 512 字节是 BIOS 用来加载 MBR 的,剩下的 512 字节,用来存储 MBR 执行时产生的数据。
至于为什么放在后面?是为了减少内存碎片,在操作系统启动后,这部分空间是会被覆盖的。
详见 https://www.zhihu.com/question/340358237
跳转到 0x7c00 处继续执行,在不同的代码里面有不同的处理方式
在 第一个扇区的代码中,又将自己从 0x7c00 搬至 0x90000 处,接着将可启动硬盘的后 2-5 扇区搬至 0x90200 处,详见 linux 0.11 源码分析#跳转至 0x9c000 处对内存进行分配
Arm 侧
当 Arm 处理器上电后,首先会运行 BootRom,BootRom 会初始化处理器并加载 Bootloader。
TODO
在 s5pv210 Arm 中,iRom 内部的代码是原厂固定好的,会通过 IO 选择从哪里启动,例如是 eMMC,则会自然而然的去 eMMC 中找启动程序,找到后(uboot-spl),会将这 8k 的程序拷贝到 iRam 中执行。
BL1 初始化系统时钟,UART,SDRAM 等设备,然后 u-boot 并跳转到 u-boot 中执行,这样 u-boot 就真正的起来了。
第二阶段 bootloader
是一个小的裸机程序,它的的主要任务是加载内核镜像并将其解压到内存中。常见的 bootloader 有 u-boot、redboot 等。
后面分析 u-boot
TODO 待分析
bootm Legacy-uImage加载地址 ramdisk加载地址 dtb加载地址
在 u-boot 中,使用 bootm 命令将 kernel、dtb 载入,并搬运至指定地址(此指定地址受限于 芯片手册中的 SDRAM 的首地址,一定要在手册中的 SDRAM 地址限定范围内)
在 boot_jump_linux 中,配置内核启动之前对寄存器的要求:R0 为 0,R1 为 machid,R2 为 atags 或 设备树地址。
接着就是 kernel_entry(0, machid, r2) 也就是执行内核 entry point 处的代码,换句话说,就跳转到了 kernel 中。
第三阶段 kernel 初始化阶段
当前内核版本 5.17
进入 kernel 的限制条件
- CPU 必须处于 supervisor 模式,并且 IRQ 和 FIQ 中断都是禁止的。
- MMU(内存管理单元必须是关闭的),此时就是物理地址
- Data cache 必须是关闭的
- Instruction cache 可以是打开的,也可以是关闭的,没有强制要求
- CPU 寄存器 R0 必须为 0
- CPU 寄存器 R1 必须为 Arm Linux Machine Type (machid)
- CPU 寄存器 R2 必须为 atag 或设备树地址
在 head.S 中主要功能如下,按照从前往后的顺序
- 进入到 kernel 后,此时 R1 值为 machid、R2 值为 atags 或设备树地址。
- 获取 phys_offset 到 R8、cpuid 到 R9、procinfo 到 R10 寄存器
- R8 为 phys_offset
- R9 为 cpuid
- R10 为 procinfo
__vet_atags
校验 atags 的有效性__create_page_tables
创建页表- 在使能 mmu 之后跳转至
__mmap_switched
__mmap_switched
在 head-common.S 中- 其主要功能
- 解压/拷贝到 RAM 中
- clear .bss
- 获取 swapper 的 task_init
- 保存 processor ID、machine type、atags pointer、control register value
- 如果定义了 CONFIG_KASAN 选项,则要开启 kasan_early_init
- 最后跳转到 start_kernel
__enable_mmu
使能 mmu
在 init/main.c
的 start_kernel
中,主要的功能如下